﻿//--------------------------------------------------------------------
// 动态报表 v1.00
//
// 开发人： 朱明智
// 创建时间： 2019-08-22
//
// 修改人：
// 修改时间：
//--------------------------------------------------------------------
using Dynamic.Controller;
using Microsoft.Reporting.WinForms;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Dynamic
{
    public interface IDynamicReport
    {
        void SetReportReportViewer(ReportViewer reportViewer);

        void AddData<T>(IEnumerable<T> data);

        void AddData(DataTable dataTable);

        void ShowReport();

        void LoadReport(string reportPath);

        void SetColoumStyle(IEnumerable<ReportColumnStyle> coloumStyle);

        //void SetDisPlayColoum(IEnumerable<string> colList);
    }

    public class DynamicReport : ReportController, IDynamicReport
    {
        #region 空白文档

        // 修正值
        protected float CorrectionValue = 0.02f;

        /// <summary>
        /// 页面设置
        /// </summary>
        protected PageSettings PageSettings { get; set; }

        /// <summary>
        /// 页面高（不含Margin）
        /// </summary>
        public float Height
        {
            get { return PageHeight - TopMargin - BottomMargin; }
        }

        /// <summary>
        /// 页面宽（不含Margin）
        /// </summary>
        public float Width
        {
            get { return PageWidth - (2 * LeftMargin); }
        }

        /// <summary>
        /// 页面高（含Margin）
        /// </summary>
        public float PageHeight
        {
            get
            {
                if (PageSettings == null)
                    return 29.7f;
                if (PageSettings.Landscape)
                    return (float)ToMillimeter(PageSettings.PaperSize.Width) / 10 - CorrectionValue;

                return (float)ToMillimeter(PageSettings.PaperSize.Height) / 10 - CorrectionValue;
            }
        }

        /// <summary>
        /// 页面宽（含Margin）
        /// </summary>
        public float PageWidth
        {
            get
            {
                if (PageSettings == null)
                    return 21f;
                if (PageSettings.Landscape)
                    return (float)ToMillimeter(PageSettings.PaperSize.Height) / 10 - CorrectionValue;

                return (float)ToMillimeter(PageSettings.PaperSize.Width) / 10 - CorrectionValue;
            }
        }

        public float LeftMargin
        {
            get
            {
                return PageSettings == null ? 1f : (float)ToMillimeter(PageSettings.Margins.Left) / 10;
            }
        }

        public float RightMargin
        {
            get
            {
                return PageSettings == null ? 1f : (float)ToMillimeter(PageSettings.Margins.Right) / 10;
            }
        }

        public float TopMargin
        {
            get
            {
                return PageSettings == null ? 1f : (float)ToMillimeter(PageSettings.Margins.Top) / 10;
            }
        }

        public float BottomMargin
        {
            get
            {
                return PageSettings == null ? 1f : (float)ToMillimeter(PageSettings.Margins.Bottom) / 10;
            }
        }

        private double ToMillimeter(int hundrethsOfInch)
        {
            return PrinterUnitConvert.Convert((double)hundrethsOfInch, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter) / 10.0;
        }


        /// <summary>
        /// 空白文档的xml文件
        /// </summary>
        protected string _docTemplate = TemplateHelper.ReportTemp;

        /// <summary>
        /// 页眉
        /// </summary>
        private PageHeader _pageHeader;

        /// <summary>
        /// 页脚
        /// </summary>
        private PageFooter _pageFooter;

        #endregion

        /// <summary>
        /// ReportView
        /// </summary>
        private ReportViewer _report;

        /// <summary>
        /// 列头样式
        /// </summary>
        private readonly List<ReportColumnStyle> _coloumStyle = new List<ReportColumnStyle>();

        private readonly List<ReportItemPattern> _reportItemPatterns = new List<ReportItemPattern>();

        private readonly List<ReportItemPattern> _reportHeadPatterns = new List<ReportItemPattern>();

        // TODO: 暂时只支持Table类型
        private ReportType ReportType { get; set; }

        public DynamicReport()
        {
            this.ReportType = ReportType.Tables;
        }

        public DynamicReport(PageSettings settings)
            : this()
        {
            PageSettings = settings;
        }

        /// <summary>
        /// 从现有报表中加载报表并进行修改
        /// </summary>
        /// <param name="url"></param>
        public void LoadReport(string url)
        {
            _docTemplate = File.ReadAllText(url);
        }

        public void SetReportReportViewer(ReportViewer reportViewer)
        {
            this._report = reportViewer;
        }

        public void SetColoumStyle(IEnumerable<ReportColumnStyle> coloumStyle)
        {
            this._coloumStyle.AddRange(coloumStyle);
        }

        /// <summary>
        /// 报表数据源
        /// </summary>
        private DataTable dataSource;

        // 列数过多是否折行显示
        private bool isNewLine;


        /// <summary>
        /// 表格列数过多是否设置折行显示, 折行后没有设置列宽的列默认1cm宽
        /// </summary>
        /// <param name="isNewLine"></param>
        public void SetTableNewLine(bool isNewLine)
        {
            this.isNewLine = isNewLine;
        }

        public void AddData<T>(IEnumerable<T> data)
        {
            if (data.Count() > 0)
            {
                this.dataSource = new DataTable();
                var props = typeof(T).GetProperties();
                foreach (var item in props)
                {
                    this.dataSource.Columns.Add(new DataColumn()
                    {
                        ColumnName = item.Name,
                        DataType = item.PropertyType,
                    });
                }

                foreach (var item in data)
                {
                    object[] row = new object[props.Length];
                    for (int i = 0; i < props.Length; i++)
                    {
                        row[i] = props[i].GetValue(item, null);
                    }
                    this.dataSource.Rows.Add(row);
                }
            }
        }

        public void AddData(DataTable dataTable)
        {
            if (dataTable != null)
            {
                this.dataSource = dataTable;
            }
        }

        public void AddHeader(PageHeader header)
        {
            _pageHeader = header;
            _pageHeader.Parent = this;
        }

        public void AddFooter(PageFooter footer)
        {
            _pageFooter = footer;
            _pageFooter.Parent = this;
        }

        /// <summary>
        /// 增加一个报表
        /// </summary>
        protected void AddReportItemPattern()
        {
            var fields = new StringBuilder();
            var columns = new StringBuilder();
            var tablixHearders = new StringBuilder();
            var tablixCells = new StringBuilder();
            var tablixMembers = new StringBuilder();
            var currentNamePrefix = _reportItemPatterns.Count + _reportHeadPatterns.Count + 1;
            var tableWidth = 0F;
            var dataRows = dataSource.Rows.Count; //数据行数
            var coloumNames = new List<string>();

            foreach (DataColumn col in dataSource.Columns)
            {
                coloumNames.Add(col.ColumnName);
            }

            // 转行处理
            if (isNewLine)
            {
                var setCols = _coloumStyle.Where(p => coloumNames.Any(u => u == p.ColumnName)).ToList();
                int colCount = 0;

                setCols.ForEach(x => x.ColumnWidth = x.ColumnWidth == 0 ? x.ColumnWidth = 1 : x.ColumnWidth);
                //setCols = setCols.OrderByDescending(x => x.ColumnWidth).ToList();
                
                float width = 0;
                foreach (var col in setCols)
                {
                    width += col.ColumnWidth;
                    if (width <= Width)
                        colCount++;
                    else
                    {
                        width -= col.ColumnWidth;
                        break; 
                    }
                }
                

                int rowCount = (int)Math.Ceiling(dataSource.Columns.Count *1.0 / colCount);

                var newDataSource = new DataTable();

                _coloumStyle.Clear();
                coloumNames.Clear();
             
                for (int i = 0; i < colCount; i++)
                {
                    string colName = string.Format("DataCol{0}", i);
                  
                    setCols[i].ColumnName = colName;
                    _coloumStyle.Add(setCols[i]);

                    newDataSource.Columns.Add(colName, typeof(string));
                    coloumNames.Add(colName);
                }

                foreach (DataRow row in dataSource.Rows)
                {
                    for (int j = 0; j < rowCount; j++)
                    {
                        object[] header = new object[colCount];
                        object[] rowData = new object[colCount];
                        for (int i = 0; i < colCount; i++)
                        {
                            if ((i + j* colCount) < row.ItemArray.Length)
                            {
                                header[i] = dataSource.Columns[i + j* colCount].ColumnName;
                                rowData[i] = row.ItemArray[i + j* colCount].ToString();
                            }
                            else
                            {
                                header[i] = "";
                                rowData[i] = "";
                            }
                        }
                        var row1 = newDataSource.Rows.Add(header);
                        var row2 = newDataSource.Rows.Add(rowData);
                    }
                }
                dataSource = newDataSource;
            }

            // 找到指定列宽的列
            var fixCols = _coloumStyle.Where(p => p.ColumnWidth != 0f && coloumNames.Any(u => u == p.ColumnName)).ToArray();

            float fixTotalWidth = fixCols.Sum(p => p.ColumnWidth);
            if (fixTotalWidth > Width)
            {
                throw new Exception("所指定的列宽大于纸张的宽(PageWidth - LeftMargin - RightMargin)");
            }
           
            // 自动分配列宽
            float autoWidth = (Width - fixCols.Sum(p => (float?)p.ColumnWidth).GetValueOrDefault()) / (coloumNames.Count - fixCols.Length) - 0.01f;

            foreach (var columnName in coloumNames)
            {
                var textAlign = TextAlign.Right;
                string hidden = "false";
                float columnWidth = autoWidth;
                var reportColumnStyle = _coloumStyle.FirstOrDefault(r => r.ColumnName == columnName);

                // 手动设置列宽优先级高
                if (reportColumnStyle != null)
                {
                    textAlign = reportColumnStyle.TextAlign;
                    if (isNewLine)
                    {
                        hidden = "true";
                    }
                    else
                    {
                        hidden = reportColumnStyle.HeaderVisible ? "false" : " true";
                    }
                    // 列宽指定为0 或 不指定 则自动分配列宽
                    if (reportColumnStyle.ColumnWidth != 0f) columnWidth = reportColumnStyle.ColumnWidth;
                }

                tableWidth += columnWidth;
                var bottomBorder = string.Empty; //每个单元格底部border
                if (dataRows == 0)
                {
                    bottomBorder = "<BottomBorder><Style>None</Style></BottomBorder>";
                }
                var coloumValue = columnName;

                //例外,如果coloumName包含Coloum之类的字段,则将value设成空
                if (columnName.IndexOf("Column", System.StringComparison.Ordinal) > -1)
                {
                    coloumValue = "　";
                }

                fields.AppendFormat(
                    "<Field Name=\"{0}\"><DataField>{0}</DataField><rd:TypeName>System.String</rd:TypeName></Field>",
                    columnName);
                columns.AppendFormat("<TablixColumn><Width>{0}cm</Width></TablixColumn>", columnWidth);
                tablixHearders.AppendFormat("<TablixCell>" +
                                            "    <CellContents>" +
                                            "        <Textbox Name=\"Textbox{0}{1}\">" +
                                            "            <CanGrow>true</CanGrow>" +
                                            "            <KeepTogether>true</KeepTogether>" +
                                            "            <Paragraphs>" +
                                            "                <Paragraph>" +
                                            "                    <TextRuns>" +
                                            "                        <TextRun>" +
                                            "                            <Value>{2}</Value>" +
                                            "                            <Style>" +
                                            "                                <FontFamily>{4}</FontFamily>" +
                                            "                            </Style>" +
                                            "                        </TextRun>" +
                                            "                    </TextRuns>" +
                                            "                    <Style>" +
                                            "                        <TextAlign>Center</TextAlign>" +
                                            "                    </Style>" +
                                            "                </Paragraph>" +
                                            "            </Paragraphs>" +
                                            "            <rd:DefaultName>Textbox{0}{1}</rd:DefaultName>" +
                                            "            <Visibility>" +
                                            "                <Hidden>{5}</Hidden>" +
                                            "            </Visibility>" +
                                            "            <Style>" +
                                            "                <Border>" +
                                            "                    <Color>Black</Color>" +
                                            "                    <Style>Solid</Style>" +
                                            "                </Border>{3}" +
                                            "                <PaddingLeft>2pt</PaddingLeft>" +
                                            "                <PaddingRight>2pt</PaddingRight>" +
                                            "                <PaddingTop>2pt</PaddingTop>" +
                                            "                <PaddingBottom>2pt</PaddingBottom>" +
                                            "            </Style>" +
                                            "        </Textbox>" +
                                            "    </CellContents>" +
                                            "</TablixCell>",
                                            columnName, currentNamePrefix, coloumValue, bottomBorder, "黑体", hidden);
                tablixCells.AppendFormat(
                    "<TablixCell>" +
                    "    <CellContents>" +
                    "        <Textbox Name=\"{0}{1}1\">" +
                    "            <CanGrow>true</CanGrow>" +
                    "            <KeepTogether>true</KeepTogether>" +
                    "            <Paragraphs>" +
                    "                <Paragraph>" +
                    "                    <TextRuns>" +
                    "                        <TextRun>" +
                    "                            <Value>=Fields!{0}.Value</Value>" +
                    "                             <Style>" +
                    "                                <FontFamily>{3}</FontFamily>" +
                    "                            </Style>" +
                    "                        </TextRun>" +
                    "                    </TextRuns>" +
                    "                    <Style>" +
                    "                        <TextAlign>{2}</TextAlign>" +
                    "                    </Style>" +
                    "                </Paragraph>" +
                    "            </Paragraphs>" +
                    "            <rd:DefaultName>{0}{1}1</rd:DefaultName>" +
                    "            <Style>" +
                    "                <Border>" +
                    "                    <Color>Black</Color>" +
                    "                    <Style>Solid</Style>" +
                    "                </Border>" +
                    "                <PaddingLeft>2pt</PaddingLeft>" +
                    "                <PaddingRight>2pt</PaddingRight>" +
                    "                <PaddingTop>2pt</PaddingTop>" +
                    "                <PaddingBottom>2pt</PaddingBottom>" +
                    "            </Style>" +
                    "        </Textbox>" +
                    "    </CellContents>" +
                    "</TablixCell>",
                    columnName, currentNamePrefix, textAlign, "黑体");

                tablixMembers.AppendFormat("<TablixMember />");
            }

            //计算表格应该离左边多少距离
            float leftPosition = 0F;
            if (tableWidth < Width)
            {
                leftPosition = (Width - tableWidth) / 2;
                if (leftPosition < 0.01f) leftPosition = 0f;
            }

            var dataSetName = string.Format("Data{0}", _reportItemPatterns.Count + _reportHeadPatterns.Count + 1);
            var reportItemPattern = new ReportItemPattern();
            reportItemPattern.Data = DynamicReportExtension.RemoveZeroData(dataSource);
            reportItemPattern.DataSetName = dataSetName;

            reportItemPattern.DataSetString = reportItemPattern.DataSetPattern
                                 .Replace("@DataSetName", dataSetName)
                                 .Replace("@Fields", fields.ToString());

            reportItemPattern.TablixString = reportItemPattern.TablixPattern
                                 .Replace("@DataSetName", dataSetName)
                                 .Replace("@TablixColumns", columns.ToString())
                                 .Replace("@TablixHeader", tablixHearders.ToString())
                                 .Replace("@TablixCells", tablixCells.ToString())
                                 .Replace("@TablixMember", tablixMembers.ToString())
                                 .Replace("@TopPosition", (0.1f).ToString())
                                 .Replace("@LeftPostion", leftPosition.ToString());

            //读取行数,如果是空行就加到新的
            if (dataRows == 0)
            {
                _reportHeadPatterns.Add(reportItemPattern);
            }
            else
            {
                _reportItemPatterns.Add(reportItemPattern);
            }
        }


        /// <summary>
        /// 最终显示报表
        /// </summary>
        public void ShowReport()
        {
            AddReportItemPattern();
            //将每一个patter转换
            if (_reportItemPatterns.Count > 0)
            {
                var dataSetsString = new StringBuilder();
                var tablixString = new StringBuilder();

                foreach (var reportItemPattern in _reportItemPatterns)
                {
                    dataSetsString.Append(reportItemPattern.DataSetString);
                    tablixString.Append(reportItemPattern.TablixString);
                }
                foreach (var reportItemPattern in _reportHeadPatterns)
                {
                    dataSetsString.Append(reportItemPattern.DataSetString);
                    tablixString.Append(reportItemPattern.TablixString);
                }

                var reportTitleString = new StringBuilder();

                if (_pageFooter != null)
                    _docTemplate = _docTemplate.Replace("@PageFooter", _pageFooter.GetTempStr());
                if (_pageHeader != null)
                    _docTemplate = _docTemplate.Replace("@PageHeader", _pageHeader.GetTempStr());


                _docTemplate = _docTemplate.Replace("@PageHeight", PageHeight.ToString())
                    .Replace("@PageWidth", PageWidth.ToString())
                    .Replace("@LeftMargin", LeftMargin.ToString())
                    .Replace("@RightMargin", RightMargin.ToString())
                    .Replace("@TopMargin", TopMargin.ToString())
                    .Replace("@BottomMargin", BottomMargin.ToString())
                    .Replace("@Width", (PageWidth - LeftMargin * 2).ToString());

                //把文档中的文字替换掉
                switch (ReportType)
                {
                    case ReportType.Tables:
                        _docTemplate = _docTemplate.Replace("@DataSets", dataSetsString.ToString())
                                                   .Replace("@Tablix", tablixString.ToString())
                                                   .Replace("@Title", reportTitleString.ToString());
                        break;

                    case ReportType.Chart:
                        break;

                    case ReportType.Finally:
                        //替换datasetstring
                        var pos = _docTemplate.IndexOf("<Body>", StringComparison.Ordinal);
                        _docTemplate = _docTemplate.Insert(pos,
                                                           string.Format(
                                                               "<DataSources><DataSource Name=\"DummyDataSource\"><ConnectionProperties><DataProvider>SQL</DataProvider><ConnectString /></ConnectionProperties><rd:DataSourceID>3eecdab9-6b4b-4836-ad62-95e4aee65ea8</rd:DataSourceID></DataSource></DataSources><DataSets>{0}</DataSets>",
                                                               dataSetsString));
                        //替换Tablix
                        pos = _docTemplate.IndexOf("<ReportItems>", StringComparison.Ordinal);
                        _docTemplate = _docTemplate.Insert(pos + 13, tablixString.ToString());
                        //替换title
                        _docTemplate = _docTemplate.Insert(pos + 13, reportTitleString.ToString());
                        break;
                }

                var doc = new XmlDocument();
                doc.LoadXml(_docTemplate);
                Stream stream = GetRdlcStream(doc);

                //加载报表定义
                _report.LocalReport.LoadReportDefinition(stream);
                _report.LocalReport.DataSources.Clear();
                foreach (var reportItemPattern in _reportItemPatterns)
                {
                    _report.LocalReport.DataSources
                           .Add(new ReportDataSource(reportItemPattern.DataSetName + "Data",
                                                     reportItemPattern.Data));
                }
                foreach (var reportItemPattern in _reportHeadPatterns)
                {
                    _report.LocalReport.DataSources
                           .Add(new ReportDataSource(reportItemPattern.DataSetName + "Data",
                                                     reportItemPattern.Data));
                }

                _report.LocalReport.Refresh();
            }
        }

        /// <summary>
        /// 序列化到内存流
        /// </summary>
        /// <returns></returns>
        protected Stream GetRdlcStream(XmlDocument xmlDoc)
        {
            Stream ms = new MemoryStream();
            XmlSerializer serializer = new XmlSerializer(typeof(XmlDocument));
            serializer.Serialize(ms, xmlDoc);

            ms.Position = 0;
            return ms;
        }
    }
}